using System;
using System.Collections;
using System.Collections.Generic;

namespace MonsterNet
{
	[Serializable]
	public class MNQueue : IEnumerable //, IEnumerator 
	{
		private List<MNQueuedFile> _queue;	//list of files awaiting to be copied
		private List<string> _extensionToIgnore;
		
		public MNQueue()  {
			_queue = new List<MNQueuedFile>();
		}
		
		
		private ulong SizeToFile(int fileNumber) {
			ulong totalSize = 0; 
			if(fileNumber > _queue.Count) fileNumber = _queue.Count;
			
			for (int i = 0; i < fileNumber; i++) {
				totalSize += _queue[i].SizeInBytes;
			}
			return totalSize;
		}
		
		public string QueueSize() {
			return QueueSizeToFile(_queue.Count);
		}
		
		public string QueueSizeToFile(int fileNumber) {
			return MNGlobal.GetSizeFromUlongToString(SizeToFile(fileNumber));
		}
		
		public double PercentSizeCompleteAtFile(int fileNumber) {
			return (double)this.SizeToFile(fileNumber) / (double)this.SizeToFile(_queue.Count);
		}

		public string GetETA(DateTime startTime, int file) {
			string result = @"na";
				
			if(file > 0) {
				TimeSpan span = DateTime.Now.Subtract(startTime);	
				double totalTimeNeeded = PercentSizeCompleteAtFile(file);
				totalTimeNeeded = (double)span.TotalSeconds / totalTimeNeeded;
				
				int etaInSeconds = (int)(totalTimeNeeded - span.TotalSeconds);
				TimeSpan etaspan = new TimeSpan(0, 0, etaInSeconds);
				
				DateTime etaDateTime = DateTime.Now.Add(etaspan);
				result = etaDateTime.ToShortTimeString();
			}
			
			return result;
		}

		
		#region Queue Enumerator
		//private int _position = -1;
		
		public IEnumerator GetEnumerator() {
			return (IEnumerator)_queue.GetEnumerator();
		}
		
		/*
		public object Current {
			get { return _queue[_position]; }
		} 
		
		public bool MoveNext() {
			_position ++;
			
			if(_position < _queue.Count) {
				return true;
			} else {
				return false;
			}
		}
		
		public void Reset() {
			_position = -1;
		}
		*/
		
		public int Count {
			get { return _queue.Count; }
		}
		
		#endregion
		
		#region create queue
		
		/// <summary>
		/// Search for files to put in the queue. 
		/// </summary>
		public bool EnqueueSynchronisedDirectory(MNSynchronisedDirectory syncDir, MNFSObjectLocation location, List<string> extensionsToIgnore) {
			this._queue.Clear();
			this._extensionToIgnore = extensionsToIgnore;
			QueueDirectory(syncDir, location);	
			
			return 	true;
		}
		
		private void QueueDirectory(MNSynchronisedDirectory syncDir, MNFSObjectLocation location) {
			if(!syncDir.Ignore) {
				QueueDirectory(syncDir, location, false, MNFSObjectLocation.NumberOfLocations, MNFSObjectLocation.NumberOfLocations);
			}
		}
		
		private void QueueDirectory(MNSynchronisedDirectory syncDir, MNFSObjectLocation location,
		                            bool ParentSync,
		                            MNFSObjectLocation ParentLocationFrom, MNFSObjectLocation ParentLocationTo) {
							
			// check to see if Parent locationTo and locationFrom are already set.
			if(ParentSync)
			{
				//set all files to copy from locationFrom to location To
				QueueAllFilesInDirectory(syncDir, ParentLocationFrom, ParentLocationTo);
				QueueAllDirectoriesInDirectory(syncDir, location, ParentLocationFrom, ParentLocationTo);
			} else {
				// see if the directory is set to sync
				MNFSObjectLocation locationTo;
				MNFSObjectLocation locationFrom;
				
				if(QueueSynchronisedFSObject(syncDir, location, out locationFrom, out locationTo)) {
					//directory is set to sync
					//sync all non private files and directories
					QueueAllFilesInDirectory(syncDir, locationFrom, locationTo);	
					QueueAllDirectoriesInDirectory(syncDir, location, locationFrom, locationTo);				
					
				} else { 
					//directory isn't set to sync, check its contents
					
					//queue files
					foreach(KeyValuePair<string, MNSynchronisedFile> kvp in syncDir.Files) {
						if(!kvp.Value.Private) {
							if(QueueSynchronisedFSObject(kvp.Value, location, out locationFrom, out locationTo)) {
								AddFileToQueue(new MNQueuedFile(kvp.Value, locationFrom, locationTo, _queue.Count));
							}							
						}
					}
					
					//queue directories
					foreach(KeyValuePair<string, MNSynchronisedDirectory> kvp in syncDir.Directories) {
						if(!kvp.Value.Private) {
							QueueDirectory(kvp.Value, location);
						}
					}						
				}		
			}
		}
		
		
		private void QueueAllFilesInDirectory(MNSynchronisedDirectory syncDir, MNFSObjectLocation locationFrom, MNFSObjectLocation locationTo) {
			foreach(KeyValuePair<string, MNSynchronisedFile> kvp in syncDir.Files) {
				if(!kvp.Value.Private 
				   && !kvp.Value.GetIsSynchronised()
				   && !kvp.Value.GetExistsAtForLocation(locationTo) 
				   && kvp.Value.GetExistsAtForLocation(locationFrom)
				   && !_extensionToIgnore.Contains(kvp.Value.Extension)
				   && !kvp.Value.Ignore) {
					AddFileToQueue(new MNQueuedFile(kvp.Value, locationFrom, locationTo, _queue.Count));
				}
			}
		}
		
		private void QueueAllDirectoriesInDirectory(MNSynchronisedDirectory syncDir, MNFSObjectLocation location, MNFSObjectLocation locationFrom, MNFSObjectLocation locationTo) {
			foreach(KeyValuePair<string, MNSynchronisedDirectory> kvp in syncDir.Directories) {
				if(!kvp.Value.Private && !kvp.Value.Ignore) {
					QueueDirectory(kvp.Value, location, true, locationFrom, locationTo);
				}
			}				
		}
			
		private bool QueueSynchronisedFSObject(MNSynchronisedFile syncObj, MNFSObjectLocation location, 
		                                   out MNFSObjectLocation locationFrom, out MNFSObjectLocation locationTo) {
			
			locationFrom = MNFSObjectLocation.NumberOfLocations;
			locationTo = MNFSObjectLocation.NumberOfLocations;
			if(!syncObj.Ignore) {
				// ?sync to location and doesn't exist at location. 
				if(syncObj.GetSynchroniseToForLocation(location) && 
				   (!syncObj.GetExistsAtForLocation(location) || syncObj is MNSynchronisedDirectory)) {
					
					// ?exists at portable
					if(syncObj.GetExistsAtForLocation(MNFSObjectLocation.Portable) || (syncObj is MNSynchronisedDirectory)) {
						//copy from portable to destination
						locationFrom = MNFSObjectLocation.Portable;
						locationTo = location;
						return true;
					} 
				} else {
					
					MNFSObjectLocation oppLocation = MNLocation.GetOppositeLocation(location);
					
					// ?sync to oppLocation and doesn't exist at oppLocation. 
					if(syncObj.GetSynchroniseToForLocation(oppLocation) && 
					   (!syncObj.GetExistsAtForLocation(oppLocation) || syncObj is MNSynchronisedDirectory)) {
						// ? doesn't exists at portable
						if(!syncObj.GetExistsAtForLocation(MNFSObjectLocation.Portable) || syncObj is MNSynchronisedDirectory) {
							//copy from portable to destination
							locationFrom = location;
							locationTo = MNFSObjectLocation.Portable;
							return true;
						} 
					}				
				}
			}			
			return false;
		}
		
		private void AddFileToQueue(MNQueuedFile qFile) {
			//check the queue for qFile
			bool exists = false;
			foreach (MNQueuedFile file in _queue) {
				if(file.RootlessPathname == qFile.RootlessPathname) {
					exists = true;
					break;
				}					
			}
			if(!exists) 
				_queue.Add(qFile);
		}
		
		/*
		private bool QueueSynchronisedDirectory(MNSynchronisedDirectory syncDir, MNFSObjectLocation location, 
		                                        out MNFSObjectLocation locationFrom, out MNFSObjectLocation locationTo) {
			
		}*/
		#endregion
		
	}
}

